Importamos las librerías y parámetros que necesitaremos en la ejecución del Notebook.
import sys
sys.path.append("..")
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
from datetime import datetime
import nltk
from nltk.tokenize.toktok import ToktokTokenizer
from nltk.corpus import stopwords
from mgmtfm import clean
from wordcloud import WordCloud
import matplotlib.pyplot as plt
pd.set_option('max_rows',9999)
pd.set_option('max_columns', 9999)
pd.set_option('display.max_colwidth', 500)
print("Notebook ejecutado el {}.".format(datetime.now().strftime("%d-%m-%Y")))
El core de nuestros datos son las llamadas en sí. Actualmente disponemos de grabaciones de un 20% de las llamadas de las que se transcriben un 20% (lo que supone un 4% del total), estas llamadas llegan mediante un proceso batch. En un futuro se espera disponer del 100% de las transcripciones en real-time.
Cargamos el dataset tal y como lo recibimos del sistema de transcripciones y visualizamos los campos:
verint_raw = pd.read_parquet('/data/datasets/input_data/verint_dataset.parquet')
verint_raw.head(1)
Vemos los campos más importantes de nuestro Dataset (que, a priori, pueden ser de utilidad):
Aunque existen más campos, estos parece que no estan relacionados con nuestro caso de uso.
Nos quedamos con parte de estos campos, para realizar un análisis previo. Y vemos el número total de llamadas disponibles:
verint = verint_raw[['co_llamada_verint','ucid','fx_evento','it_llamada','duration', 'plaintext', 'no_destino_pa']]
verint.plaintext = verint.plaintext.str.lower()
print(verint.count())
verint.head(1)
Otro dataset del que disponemos es el de monitorizaciones de las llamadas. Se trata de un cuestionario realizado por un grupo de personas sobre llamadas escuchadas. A nosotros nos interesa la parte relacionada con el tipo de la llamada, que viene identificada en la encuesta con los prefijos C y D.
monitorizaciones = pd.read_parquet('/data/datasets/input_data/monitorizaciones.parquet')
print("Tenemos {:,} llamadas monitorizadas.".format(len(np.unique(monitorizaciones["ucid"]))))
monitorizaciones.head(1)
Al igual que hemos hecho anteriormente, vemos los datos más relevantes de este Dataset:
Nos quedamos con los datos de categorización de las llamadas (prefijos C y D).
monitor = monitorizaciones[(monitorizaciones.name.str.startswith('C') | monitorizaciones.name.str.startswith('D'))].drop_duplicates()
diccionario = {'C#1':'información', 'C#2':'contratar', 'D#1':'información', 'D#2':'consulta', 'D#3':'queja', 'D#4':'trámite'}
for k in diccionario.keys():
monitor.loc[monitor.name.str.startswith(k), 'tipo'] = diccionario[k]
monitor.name = monitor.name.apply(lambda x: 'comercial' if x.startswith('C') else 'no_comercial')
monitor.dropna(inplace=True)
monitor.it_llamada = monitor.it_llamada.dt.date
monitor.columns = ['co_llamada_verint', 'ucid', 'fx_evento', 'duration', 'unidad_negocio', 'motivo_llamada', 'subtipo', 'tipo']
monitor.head(1)
Combinamos los datos de las monitorizaciones con las llamadas:
monitored_calls = pd.merge(verint, monitor, on=['co_llamada_verint', 'ucid', 'fx_evento', 'duration'], how='inner')
display(monitored_calls.head(1))
print("{:,} llamadas totales monitorizadas.".format(len(np.unique(monitored_calls["co_llamada_verint"]))))
Como hemos visto los datos de monitorizaciones tienen el problema de que son escasos, es por eso que nos planteamos analizar los datos de IVR. Estos datos son proporcionados por el usuario al llamar al call-center. En primer lugar vemos las categorias diferentes que obtenemos del dataset de llamadas:
", ".join(verint["no_destino_pa"].apply(str).unique())
Hemos obtenido una jerarquía desde el area de marketing, mostramos la más actual:
ivr_hierarchy = pd.read_excel('/data/mgm/data/dwproyp0.dwen_1004_14_etiquetas_23102019.xlsx', index_col=0, header=0)
last_date = ivr_hierarchy["fx_carga"].max()
print("Nos quedamos con la fecha: {}".format(last_date))
ivr_hierarchy = ivr_hierarchy[ivr_hierarchy["fx_carga"] == last_date].drop(columns="fx_carga")
ivr_hierarchy.columns = ["tipo", "subtipo", "no_destino_pa"]
ivr_hierarchy.head(10)
Por último utilizamos esta jerarquía para obtener los tipos de las llamadas dependiendo de su IVR.
ivr_calls = pd.merge(verint, ivr_hierarchy, on=['no_destino_pa'], how='inner')
display(ivr_calls.head(1))
print("{:,} llamadas totales con IVR.".format(len(np.unique(ivr_calls["co_llamada_verint"]))))
En este apartado vamos a hacer un pequeño estudio sobre los datos anteriormente extraidos. Lo haremos tanto con los datos de las monitorizaciones como con los datos de IVR.
df_group = (monitored_calls.groupby(['tipo', 'motivo_llamada']).count().reset_index()[['tipo',"motivo_llamada",'ucid']]).set_index(["tipo", "motivo_llamada"])
df_group.columns = [ "count"]
df_group.plot.pie(y='count', figsize=(7, 7))
display(df_group)
del(df_group)
Cargamos un fichero en el que hemos tokenizado previamente estos datos para ahorrarnos el procesamiento. Lo hacemos usando el módulo "mgmtfm" que contiene clases y funciones para simplificarnos la ejecución del proyecto.
file_tokens = "/data/mgm/data/pandas/tokens_monitored_quit_commons_12112019.pkl"
clean_steps = clean.Clean()
clean_steps.load_tokens(file_tokens)
monitored_tokens = clean_steps.tokens
Para tener una idea inicial de las llamadas que se hacen, podemos pintar una nube de palabras que nos permita visualizar de manera sencilla los términos más usados:
def plot_wordcloud(tokens, title=None):
text = " ".join(tokens["plaintext"].apply(" ".join))
wordcloud = WordCloud(background_color="white").generate(text)
fig = plt.figure( figsize=(16,10) )
if (title):
fig.suptitle(title)
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis("off")
plt.show()
plot_wordcloud(monitored_tokens)
Las palabras que nos encontramos parece que están claramente relacionadas con el servicio que se puede dar en el call center como "línea móvil", "número teléfono", "contrato", "oferta", "servicio"...
Algo interesante puede ser obtener la nube de palabras por cada categoría para ver si obtenemos diferencias.
tipos = clean_steps.distribucion_tipos.index.tolist()
for tipo in tipos:
tokens_cat = monitored_tokens[monitored_tokens['tipo']==tipo]
plot_wordcloud(tokens_cat, "Categoría: " + tipo.upper() )
Vemos que efectivamente hay variaciones en las nubes de palabras de las categorías, por destacar algunas:
Realizamos el mismo ejercicio para las categorizaciones con IVR. En primer lugar mostramos su distribución:
df_group = (ivr_calls.groupby(['tipo']).count().reset_index()[['tipo','ucid']]).set_index("tipo")
df_group.columns = [ "count"]
df_group.plot.pie(y='count', figsize=(7, 7))
display(df_group)
del(df_group)
De nuevo cargamos los tokens, para no tener que repetir el procesamiento y mostramos la nube de palabras.
file_tokens = "/data/mgm/data/pandas/tokens_ivr_quit_commons_02112019.pkl"
clean_steps = clean.Clean()
clean_steps.load_tokens(file_tokens)
ivr_tokens = clean_steps.tokens
plot_wordcloud(ivr_tokens)
Vemos que aparecen más términos al aumentar la cantidad de llamadas. Mostramos la nube de palabras por categorías.
tipos = clean_steps.distribucion_tipos.index.tolist()
for tipo in tipos:
tokens_cat = ivr_tokens[ivr_tokens['tipo']==tipo]
plot_wordcloud(tokens_cat, "Categoría: " + tipo.upper() )
Las categorías aparecen algo más difusas en este caso que en el de monitorizacines. Pero sí nos encontramos por ejemplo con: